import Taro from "@tarojs/taro"

import { EMPTY, of } from "rxjs";
import { tap, mergeMap, map, catchError, switchMap } from "rxjs/operators";
import { ButtonProps } from "@tarojs/components";
import { PersistState } from "@datorama/akita";
import { OrgApproveParams, PersonApproveParams, subsOrderParam, UserUpdateParams } from "@/models/request/userReq";
import { jwtDecode } from "@/utils";
import { AuthQuery, AuthStore, makeStore } from "@/state/auth/store";
import authService from "@/services/authService";
import { TokenPayload, LoginResult, TokenInfo, AccessItem } from "@/models/Auth"
import subsService from "@/services/subsService";
import storage from "@/utils/storage";
import { ORG_LOGIN } from "@/constants/user";
import { APP_RESTRICT } from "@/constants/subscription";

// 类似于仓储Repository， 但也是门面设计模式，引入store和api请求等相关实现，对页面组件隐藏复杂实现，也是前端中的view model
export class AuthFacade {
  orgId$(orgId$: any, arg1: string): [any] {
    throw new Error('Method not implemented.');
  }
  //HACK 不完美，用于储存全局token
  private app = Taro.getApp();

  constructor(private store: AuthStore, private query: AuthQuery) {
    //箭头函数不需要bind作用域
    // this.checkLogin = this.checkLogin.bind(this);
  }

  readonly jsCode$ = this.query.jsCode$;
  readonly token$ = this.query.token$;
  readonly loginStatus$ = this.query.loginStatus$;
  readonly userId$ = this.query.userId$;
  readonly realName$ = this.query.realName$;
  readonly orgs$ = this.query.orgs$;
  readonly userInfo$ = this.query.userInfo$;
  readonly subscription$ = this.query.subscription$;

  readonly isLoading$ = this.query.selectLoading();

  readonly userApproveStatus$ = this.query.userApproveStatus$;
  readonly orgApproveStatus$ = this.query.orgApproveStatus$;

  readonly sexual$ = this.query.sexual$;
  readonly desc$ = this.query.desc$;
  //身份证号
  readonly idCardNo$ = this.query.idCardNo$;

  /**
   * 定期检查token有效性，jwt 一般有效期一周，过期前可以通过refresh_token刷新
   * @param 
   * @returns tokenInfo
   */
  checkLogin = () => {
    const token = this.query.token;
    if (!token) {
      console.warn("token不存在，需要重新登录")
      return EMPTY;
    } else {
      const token_payload: TokenPayload = JSON.parse(
        jwtDecode(this.query.token).payload
      )

      const refresh_payload: TokenPayload = JSON.parse(
        jwtDecode(this.query.refreshToken).payload
      )

      //token时间差，直接拿this.query.expires_in的值不是最新的，费解
      const timeGap = token_payload.exp * 1000 - new Date().getTime();
      //refresh_token时间差
      const timeGap1 = refresh_payload.exp * 1000 - new Date().getTime();

      //如果refresh_token都已经过期，不需要刷新续约token，需要重新登录
      if (timeGap1 < 0) {
        console.warn("refresh_token过期，需要重新登录")
        return EMPTY;
      }

      // 只需要处理将在2小时内过期的token，未过期
      if (timeGap >= 2 * 3600 * 1000) {
        console.warn("token有效期大于两小时，不需要刷新")
        this.store.update(state => {
          state.loginStatus = true;
        })
        //写入全局token
        this.app.token = token;

        //此处返回的意义仅仅是为了上层调用能继续执行IM登录逻辑
        return of({} as TokenInfo)
      } else {
        //过期或快过期则刷新token
        return this.refreshToken(this.query.refreshToken);
      }
    }
  }

  /**
   * 后端检查token似乎多余，暂时废弃不用
   * 检查token有效性，只在需要校验token合法性时使用
   * @param
   * @returns tokenInfo
   */
  checkToken = () => {
    const token = this.query.token;
    if (!token) {
      // 此处抛出异常是为了进入下一步的处理
      this.store.update(state => {
        state.loginStatus = false;
      })
      // 传入值有问题，无需刷新refreshToken
      console.warn("token不存在");
      return EMPTY;
    } else {
      return authService.checkLoginSvc(token)
        .pipe(
          mergeMap(checkInfo => {
            // 取当前时间为默认值
            let expires = new Date().getTime();

            if (checkInfo.active) {
              this.store.update(state => {
                state.loginStatus = true;
                state.userId = checkInfo.sub;
                state.roles = checkInfo.roles;
                state.expires_in = checkInfo.exp;
              })
              expires = checkInfo.exp;
            } else {
              // 这是冷启动后token已经过期， 要判断refresh_token的有效期
              const refresh_payload: TokenPayload = JSON.parse(
                jwtDecode(this.query.refreshToken).payload
              );
              if (new Date().getTime() > refresh_payload.exp) {
                this.store.update(state => {
                  state.loginStatus = false;
                })
              } else {
                // 确保能触发刷新token逻辑
                expires = refresh_payload.exp;
              }
            }
            const timeGap = expires - new Date().getTime();

            // 只需要处理将在2小时内过期的token，未过期
            if (timeGap >= 2 * 3600 * 1000) {
              return EMPTY;
            } else {
              return this.refreshToken(this.query.refreshToken)
            }
          })
          // token快过期了就去刷新refresh_token
          // definedOrEmpty,
          // switchIfEmpty(this.refreshToken(tokenInfo.refresh_token))
        )
    }
  }

  /**
   * 获取更多个人信息，在需要展示个人信息时调用
   */
  getMe = () => {
    return authService.getMeSvc()
      .pipe(
        tap(meRsp => {
          const orgLogin = storage.get(ORG_LOGIN);
          const isvalid = orgLogin && meRsp.orgList.some(item => item.orgId === orgLogin)
          //如果被移除了当前登录机构的关联关系，则需要重新登录
          if (orgLogin && !isvalid) {
            this.logout();
          }

          //根据结果更新store中相应的字段
          this.store.update(state => {
            state.realName = meRsp.realName;
            state.idCardNo = meRsp.idCardNo;
            state.userInfo.nickName = meRsp.nickname;
            state.userInfo.avatarUrl = meRsp.avatar;
            state.orgs = meRsp.orgList;
            state.desc = meRsp.desc;
            state.sexual = meRsp.sexual;
          })
        })
      )
  }
  wxBindLite = () => {
    return authService.wxBindLiteSvc()
      .pipe(
        map(this.updateStore)
      )
  }

  wxBindPro = (e: any) => {
    console.log("微信授权登录点击信息: ", e)
    return authService.wxBindProSvc()
      .pipe(
        tap(this.updateStore)
      )
  }

  wxBindPhone = (detail: ButtonProps.onGetPhoneNumberEventDetail) => {
    return authService.wxBindPhoneSvc(this.query.jsCode.code, detail)
      .pipe(
        //以下逻辑的作用是， 后端请求就算失败，js_code被用过就失效，需要在点击按钮之前重新获取js_code
        catchError(() => {
          //使用过的js_code要作废，就算报错
          this.store.update(state => {
            state.jsCodeObj = { code: "", expires: new Date().getTime() };
          })

          //重新获取一个js_code，并抛出错误以便重新点击
          return this.wxLogin()
            .pipe(
              mergeMap(() => {
                throw new Error("服务器错误, 清空js_code");
              })
            )
        }),
        tap(this.updateStore)
      )
  }

  // todo 需要判断refresh_token过期的情形
  refreshToken = (refresh_token: string) => {
    return authService.refreshTokenSvc(refresh_token)
      .pipe(
        tap(tokenInfo => {
          //写入全局token
          this.app.token = tokenInfo.access_token;

          // 更新token信息
          this.store.update(state => {
            state.loginStatus = true;
            state.token = tokenInfo.access_token;
            state.refresh_token = tokenInfo.refresh_token;
            state.expires_in = new Date().getTime() + tokenInfo.expires_in * 1000;
          })
        })
      )
  }

  logout = () => {
    this.store.reset();
  }

  /**
   * 小程序冷启动调用一次，然后需要登录的逻辑再调用一次
   * @returns js_code
   */
  wxLogin = () => {
    const codeObj = this.query.jsCode;

    return authService.wxLoginSvc(codeObj)
      .pipe(
        tap(resCode => {
          //如果js_code发生变化则更新state
          if (codeObj.code !== resCode.code) {
            this.store.update(state => {
              state.jsCodeObj = resCode;
            })
          }
        })
      )
  }

  //抽取重复代码封装为公共方法
  updateStore = (loginRes: LoginResult) => {
    //写入全局token
    this.app.token = loginRes.tokenInfo.access_token;

    // 本地解密token的payload信息
    const payload: TokenPayload = JSON.parse(
      jwtDecode(loginRes.tokenInfo.access_token).payload
    );

    //重新登录则需要移除org选择状态
    storage.remove(ORG_LOGIN);

    this.store.update(state => {
      //使用过的js_code要作废
      state.jsCodeObj = { code: "", expires: new Date().getTime() };
      state.loginStatus = true;
      state.userId = payload.sub;
      state.roles = payload.authorities;
      state.token = loginRes.tokenInfo.access_token;
      state.refresh_token = loginRes.tokenInfo.refresh_token;
      state.expires_in = loginRes.tokenInfo.expires_in;
      state.userInfo = loginRes.userInfo;
    })
  }

  personApprove = (obj1: PersonApproveParams) => {
    // console.log(obj1, 'tttttt')
    //如果任意参数为空则不往下执行
    if (!obj1.realName || !obj1.idCardNo || !obj1.idCardFront || !obj1.idCardBack) {
      throw new Error("任意参数不能为空!");
    }
    return authService.personApproveSvc(obj1)
      .pipe(
        switchMap(this.getapproveStatus)
      )
  }

  orgApprove = (obj1: OrgApproveParams) => {
    //如果任意参数为空则不往下执行
    if (!obj1.orgName || !obj1.orgNo || !obj1.orgImg) {
      throw new Error("任意参数不能为空!");
    } else {
      return authService.orgApproveSvc(obj1)
        .pipe(
          switchMap(this.getapproveStatus)
        )
    }
  }

  //切换机构
  changeOrg = (orgId: string) => {
    return authService.changeOrgSvc(orgId)
      .pipe(
        tap(res => {
          //单独保存机构选择状态
          const res2 = storage.put(ORG_LOGIN, res.org_id);
          console.log('ORG_LOGIN的值：', res2)
          this.store.update(state => {
            state.roles = res.authorities;
            state.token = res.access_token;
            state.refresh_token = res.refresh_token;
            state.expires_in = res.expires_in;
          })
        })
      )
  }
  // 更新个人信息
  userUpdate = (obj1: UserUpdateParams) => {
    //如果任意参数为空则不往下执行
    if (!obj1.nickname && !obj1.avatar) {
      throw new Error("请至少提交一个修改项");
    } else {
      return authService.userUpdateSvc(obj1)
        .pipe(
          tap(() => {
            const nickname = obj1.nickname;
            const avatar = obj1.avatar;
            const sexual = obj1.sexual;
            const desc = obj1.desc;
            if (nickname) {
              this.store.update(state => {
                state.userInfo.nickName = nickname;
              })
            }

            if (avatar) {
              this.store.update(state => {
                state.userInfo.avatarUrl = avatar;
              })
            }
            if (sexual) {
              this.store.update(state => {
                state.sexual = sexual;
              })
            }
            if (desc) {
              this.store.update(state => {
                state.desc = desc;
              })
            }
          })
        )
    }
  }

  //获取认证状态
  getapproveStatus = () => {
    return authService.approveStatusSvc()
      .pipe(
        tap(status => {
          console.log(status, '此处是否拿到了认证状态接口放回值');
          this.store.update(state => {
            state.userApproveStatus = status.userApprovalStatus;
            state.orgApproveStatus = status.orgApprovalStatus;
          })
        })
      )
  }

  //提交激活码
  postActivateCode = (data: string) => {
    return authService.activateCodeSvc(data)
  }

  //获取支付参数
  createSubsOrders = (param: subsOrderParam) => {
    return subsService.subsOrderSvc(param)

  }

  //取消支付
  cancelOrder = (orderId: string) => {
    return subsService.cancelOrderSvc(orderId)
  }

  //关注用户、机构
  addFollow = (toUser: string, toOrg: string, ops: boolean) => {
    return authService.addFollowSvc(toUser, toOrg, ops)
  }

  //关注用户、机构
  isFollowed = (anchorId: string, orgId: string) => {
    return authService.isFollowSvc(anchorId, orgId)

  }

  //获取关注的单个主播详情
  getAnchorDetails = (userId: string) => {
    return authService.getAnchorDetailsSvc(userId)
  }

  //获取关注的单个机构详情
  getOrgDetails = (orgId: string) => {
    return authService.getOrgDetailsSvc(orgId)
  }

  // 获取关注的主播或机构的直播动态
  getLatestShow = (orgId: string, userId: string) => {
    return authService.getLatestShowSvc(orgId, userId)
  }

  // 获取套餐所有数据
  AllPlanInfo(code: string) {
    return authService.AllPlanInfoSvc(code);
  }

  //获取个人对应的订阅信息 
  getSubs = () => {
    return authService.getSubsSvc()
      .pipe(
        tap(subsRsp => {
          const subs = [] as AccessItem[];

          subsRsp.options.map(item => {
            //只处理小程序端用到的功能判断
            if (item.appRestrict.includes(APP_RESTRICT.MINI_APP)) {
              const sub = {} as AccessItem;
              sub.optionValue = item.optionValue;
              sub.optionName = item.optionName;
              subs.push(sub);
            }
          })

          //根据结果更新store中相应的字段
          this.store.update(state => {
            state.subscription = subs;
          })
        })
      )
  }

  //检查当前订阅是否包含某些权限
  checkSubs = (options: string[]) => {
    return this.subscription$
      .pipe(
        map(sub => sub.map(item => item.optionValue)),
        //检查输入的权限是否包含在当前有效订阅内
        map(optionList => options.every(item => optionList.includes(item)))
      )
  }

  //检查当前订阅是否包含某些权限，需要远程请求后端
  checkSubsRemote = (options: string[]) => {
    return authService.checkSubsSvc(options);
  }

  //提交订阅消息通知
  submitsubMsgIds(appId: string, roomId: string, tmplIds: Array<string>) {
    return authService.submitsubMsgIdsSvc(appId, roomId, tmplIds);
  }
}

export const makeFacade = (): AuthFacade => {
  const [store, query] = makeStore();

  return new AuthFacade(store, query);
};